#pragma once
#include "nnse_support.h"

namespace nnse
{
	template <class T> public ref class problem_solver_core
	{
		// Constructor and Destructor
			public: problem_solver_core();
			protected: virtual ~problem_solver_core();
		// Member Variables and their Accessor Functions
			// number_of_training_examples - the number of examples used for training purposes
				private: unsigned int number_of_training_examples;
				public: unsigned int get_number_of_training_examples();
				public: void put_number_of_training_examples(unsigned int n_e);
			// number_of_testing_examples - the number of examples used for testing purposes
				private: unsigned int number_of_testing_examples;
				public: unsigned int get_number_of_testing_examples();
				public: void put_number_of_testing_examples(unsigned int n_e);
			// current_training_example - the current example being analysed for training purposes
				private: unsigned int current_training_example;
				public: unsigned int get_current_training_example();
				public: void put_current_training_example(unsigned int c_e);
			// current_testing_example - the current example being analysed for testing purposes
				private: unsigned int current_testing_example;
				public: unsigned int get_current_testing_example();
				public: void put_current_testing_example(unsigned int c_e);
			// number_of_inputs - the number of inputs in each training or testing example
				private: unsigned int number_of_inputs;
				public: unsigned int get_number_of_inputs();
				public: void put_number_of_inputs(unsigned int n_i);
			// number_of_targets - the number of targets in each training or testing example
				private: unsigned int number_of_targets;
				public: unsigned int get_number_of_targets();
				public: void put_number_of_targets(unsigned int n_t);
			// wrap_input - the number of inputs to display before a new row is started
				private: unsigned int wrap_input;
				public: unsigned int get_wrap_input();
				public: void put_wrap_input(unsigned int w_i);
			// wrap_target - the number of targets to display before a new row is started
				private: unsigned int wrap_target;
				public: unsigned int get_wrap_target();
				public: void put_wrap_target(unsigned int w_t);
			// the following function uses training_flag to determine whether to return number_of_training_examples or number_of_testing_examples
				public: unsigned int get_number_of_examples();
			// the following function uses training_flag to determine whether to set number_of_training_examples or number_of_testing_examples
				public: void put_number_of_examples(unsigned int n_e);
			// the following function uses training_flag to determine whether to return current_training_example or current_testing_example
				public: unsigned int get_current_example();
			// the following function uses training_flag to determine whether to set current_training_example or current_testing_example
				public: void put_current_example(unsigned int c_e);
			// the following function uses input_flag to determine whether to return number_of_inputs or number_of_targets
				public: unsigned int get_number_of_elements();
			// the following function uses input_flag to determine whether to return wrap_input or wrap_target
				public: unsigned int get_wrap_element();
			// training_flag - a flag to indicate training (as opposed to testing) is in progress
				private: bool training_flag;
				public: bool get_training_flag();
				public: void put_training_flag();
				public: bool get_testing_flag();
				public: void put_testing_flag();
			// input_flag - a flag to indicate inputs (as opposed to targets) are required
				private: bool input_flag;
				public: bool get_input_flag();
				public: void put_input_flag();
				public: bool get_target_flag();
				public: void put_target_flag();
			// the following flags track what data has been loaded so far
				public: bool training_input_done;
				public: bool training_target_done;
				public: bool testing_input_done;
				public: bool testing_target_done;
			// target_comments - an array of comments that associate each target with a textual description
				public: array<System::String^>^ target_comments;
			// training_input_vector - an array of training inputs
				private: array<T>^ training_input_vector;
				public: T get_training_input_value(unsigned int i_n);
				protected: void put_training_input_value(unsigned int i_n, T v);
			// training_target_vector - an array of training targets
				private: array<T>^ training_target_vector;
				public: T get_training_target_value(unsigned int t_n);
				protected: void put_training_target_value(unsigned int t_n, T v);
			// testing_input_vector - an array of testing inputs
				private: array<T>^ testing_input_vector;
				public: T get_testing_input_value(unsigned int i_n);
				protected: void put_testing_input_value(unsigned int i_n, T v);
			// testing_target_vector - an array of testing targets
				private: array<T>^ testing_target_vector;
				public: T get_testing_target_value(unsigned int t_n);
				protected: void put_testing_target_value(unsigned int t_n, T v);
			// the following functions use training_flag and input_flag to determine which of the above get_*_*_value or put_*_*_value functions to call
				public: T get_value(unsigned int el);
				public: T get_input_value(unsigned int el);
				public: T get_target_value(unsigned int el);
				public: void put_value(unsigned int el, T v);
			// training_input_comments - an array of comments associated with the training inputs
				private: array<System::String^>^ training_input_comments;
				public: System::String^ get_training_input_comment(unsigned int ex);
				protected: void put_training_input_comment(unsigned int ex, System::String^ comment);
			// training_target_comments - an array of comments associated with the training targets
				private: array<System::String^>^ training_target_comments;
				public: System::String^ get_training_target_comment(unsigned int ex);
				protected: void put_training_target_comment(unsigned int ex, System::String^ comment);
			// testing_input_comments - an array of comments associated with the testing inputs
				private: array<System::String^>^ testing_input_comments;
				public: System::String^ get_testing_input_comment(unsigned int ex);
				protected: void put_testing_input_comment(unsigned int ex, System::String^ comment);
			// testing_target_comments - an array of comments associated with the testing targets
				private: array<System::String^>^ testing_target_comments;
				public: System::String^ get_testing_target_comment(unsigned int ex);
				protected: void put_testing_target_comment(unsigned int ex, System::String^ comment);
			// the following functions use training_flag and input_flag to determine which of the above get_*_*_comment or put_*_*_comment functions to call
				public: System::String^ get_comment();
				public: System::String^ get_input_comment();
				public: System::String^ get_target_comment();
				public: void put_comment(System::String^ comment);
		// Worker Functions
			protected: void allocate_all_storage();
			protected: void allocate_storage();
			protected: void normalise_vector();
			protected: void get_min_val(T& min_val, unsigned int& min_el);
			protected: void get_max_val(T& max_val, unsigned int& max_el);
	};
}
